﻿using System.Collections;
using System.Collections.Concurrent;
using System.Reflection;

namespace PmSoft.Core.Extensions;

/// <summary>
/// 反射相关的扩展方法
/// </summary>
public static class ReflectionExtensions
{
	private static readonly SemaphoreSlim _semaphore = new(1, 1);
	private static readonly NullabilityInfoContext _nullabilityInfoContext = new();
	private static readonly ConcurrentDictionary<Type, object> ValueTypeDefaultValueMapping = new();

	/// <summary>
	/// 判断类型是否是简单类型（值类型或字符串）
	/// </summary>
	/// <param name="type">要判断的类型</param>
	/// <returns>如果是简单类型，则为 true；否则为 false</returns>
	public static bool IsSimpleType(this Type type)
	{
		if (type.IsValueType || typeof(string) == type)
		{
			return true;
		}

		if (type.IsConstructedGenericType && type.Name.Equals("Nullable`1"))
		{
			return type.GenericTypeArguments.First().IsSimpleType();
		}
		return false;
	}

	/// <summary>
	/// 判断类型是否有默认构造函数
	/// </summary>
	/// <param name="type">要判断的类型</param>
	/// <returns>如果有默认构造函数，则为 true；否则为 false</returns>
	public static bool HasDefaultConstructor(this Type type)
	{
		return type.IsValueType || type.GetConstructor(Type.EmptyTypes) != null;
	}

	/// <summary>
	/// 获取类型的默认值
	/// </summary>
	/// <param name="type">要获取默认值的类型</param>
	/// <returns>类型的默认值</returns>
	public static object? GetDefaultValue(this Type type)
	{
		if (type.IsSimpleType())
		{
			return ValueTypeDefaultValueMapping.GetOrAdd(type, x =>
			{
				if (x.IsEnum) return 0;

				if (x == typeof(string)) return string.Empty;

				return Activator.CreateInstance(x)!;
			});
		}
		return null;
	}

	/// <summary>
	/// 获取类型的实际类型
	/// </summary>
	/// <param name="type">要获取实际类型的类型</param>
	/// <returns>实际类型</returns>
	public static Type GetRealType(this Type type)
	{
		Type? ienum = FindIEnumerable(type);
		if (ienum == null) return type;

		return ienum.GetGenericArguments()[0];
	}

	/// <summary>
	/// 判断类型是否是指定类型的子类或实现类
	/// </summary>
	/// <typeparam name="T">指定类型</typeparam>
	/// <param name="type">要判断的类型</param>
	/// <returns>如果是指定类型的子类或实现类，则为 true；否则为 false</returns>
	public static bool Is<T>(this Type type) where T : class
	{
		return typeof(T).IsAssignableFrom(type);
	}

	/// <summary>
	/// 判断类型是否继承自类
	/// </summary>
	/// <param name="type">要判断的类型</param>
	/// <returns>如果继承自类，则为 true；否则为 false</returns>
	public static bool IsInheritedClass(this Type type)
	{
		if (typeof(IEnumerable).IsAssignableFrom(type)) return false;

		if (type.GetInterfaces().Any()) return true;
		return type.IsInheritedAbstractClass();
	}

	/// <summary>
	/// 判断类型是否继承自抽象类
	/// </summary>
	/// <param name="type">要判断的类型</param>
	/// <returns>如果继承自抽象类，则为 true；否则为 false</returns>
	public static bool IsInheritedAbstractClass(this Type type)
	{
		if (type.IsAbstract) return true;
		if (type != typeof(object))
		{
			return type.BaseType!.IsInheritedAbstractClass();
		}
		return false;
	}

	/// <summary>
	/// 判断属性是否可空
	/// </summary>
	/// <param name="property">属性信息</param>
	/// <returns>如果可空，则为 true；否则为 false</returns>
	public static bool IsNullable(this PropertyInfo property)
	{
		try
		{
			_semaphore.Wait();
			var nullabilityInfo = _nullabilityInfoContext.Create(property);
			return nullabilityInfo.WriteState == NullabilityState.Nullable;
		}
		finally
		{
			_semaphore.Release();
		}
	}

	/// <summary>
	/// 判断字段是否可空
	/// </summary>
	/// <param name="field">字段信息</param>
	/// <returns>如果可空，则为 true；否则为 false</returns>
	public static bool IsNullable(this FieldInfo field)
	{
		try
		{
			_semaphore.Wait();
			var nullabilityInfo = _nullabilityInfoContext.Create(field);
			return nullabilityInfo.WriteState == NullabilityState.Nullable;
		}
		finally
		{
			_semaphore.Release();
		}
	}

	/// <summary>
	/// 查找类型实现的 IEnumerable 接口
	/// </summary>
	/// <param name="type">要查找的类型</param>
	/// <returns>实现的 IEnumerable 接口类型</returns>
	private static Type? FindIEnumerable(Type type)
	{
		if (type == null || type == typeof(string)) return null;

		if (type.IsArray)
		{
			return typeof(IEnumerable<>).MakeGenericType(type.GetElementType()!);
		}

		if (type.IsGenericType)
		{
			foreach (Type arg in type.GetGenericArguments())
			{
				Type ienum = typeof(IEnumerable<>).MakeGenericType(arg);

				if (ienum.IsAssignableFrom(type))
				{
					return ienum;
				}
			}
		}

		Type[] ifaces = type.GetInterfaces();

		if (ifaces != null && ifaces.Length > 0)
		{
			foreach (Type iface in ifaces)
			{
				Type? ienum = FindIEnumerable(iface);
				if (ienum != null)
				{
					return ienum;
				}
			}
		}

		if (type.BaseType != null && type.BaseType != typeof(object))
		{
			return FindIEnumerable(type.BaseType);
		}

		return null;
	}
}



